﻿#include "stdafx.h"
#include "CaptureTool.h"
#include <algorithm>

CaptureTool capture_tool;

const auto grid_size = 1;

CaptureTool::CaptureTool()
{
	stWindowSet_.name = "魔兽世界";
}


CaptureTool::~CaptureTool()
{
}

CaptureTool& CaptureTool::inst()
{
	return capture_tool;
}

int CaptureTool::GetValueFromImg(int x, int y)
{
	const auto grid_width = 90 * 4;
	auto y_offset = y * grid_size * grid_width;
	auto x_offset = x * grid_size * 4;
	int b_value = avail_cap_[y_offset + x_offset];
	int g_value = avail_cap_[y_offset + x_offset + 1];
	int r_value = avail_cap_[y_offset + x_offset + 2];
	int a_value = avail_cap_[y_offset + x_offset + 3];
	return (b_value * 256 + g_value) * 256 + r_value;
}

void CaptureTool::GetValueFromImg(int x, int y, int& r, int& g, int &b, int& a)
{
	const auto grid_width = 90 * 4;
	auto y_offset = y * grid_size * grid_width;
	auto x_offset = x * grid_size * 4;
	b = avail_cap_[y_offset + x_offset];
	g = avail_cap_[y_offset + x_offset + 1];
	r = avail_cap_[y_offset + x_offset + 2];
	a = avail_cap_[y_offset + x_offset + 3];
}


void CaptureTool::FetchHandle()
{
	wow_hwnd_ = ProceGetWoWHwnd();
}

void CaptureTool::ResetData()
{
	wow_hwnd_ = NULL;
	avail_scr_x = 0;
	avail_scr_y = 0;
}

//回调函数的定义
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
	CaptureTool::StWindowSet *pDlong = (CaptureTool::StWindowSet*)lParam;
	char buff[128];
	GetWindowTextA(hwnd, buff, 128);
	std::string name_get = buff;
	if (name_get == pDlong->name)
	{
		CaptureTool::stWindow wind;
		wind.name = name_get;
		wind.hwnd = hwnd;
		GetWindowThreadProcessId(hwnd, &wind.pid);
		pDlong->vec_hwnd.push_back(wind);
		OutputDebugStringA(buff);
		OutputDebugStringA("\n");
	}
	return TRUE;
}

HWND CaptureTool::ProceGetWoWHwnd()
{
	HWND hWnd = NULL;
	stWindowSet_.vec_hwnd.clear();
	//传递给EnumWindowsProc的额外信息
	EnumWindows(EnumWindowsProc, (LPARAM)&stWindowSet_);
	std::sort(stWindowSet_.vec_hwnd.begin(), stWindowSet_.vec_hwnd.end(), [](const auto& lhs, const auto& rhs) {
		return lhs.hwnd < rhs.hwnd;
	});
	bool sel_succed = false;
	for (int i = 0; i < stWindowSet_.vec_hwnd.size(); i++)
	{
		if (stWindowSet_.vec_hwnd[i].pid == sel_window_pid)
		{
			hWnd = stWindowSet_.vec_hwnd[i].hwnd;
			sel_succed = true;
		}
	}
	if (sel_succed)
	{

	}
	else if (stWindowSet_.vec_hwnd.size() > 0)
	{
		sel_window_pid = 0;
		hWnd = stWindowSet_.vec_hwnd[0].hwnd;
		sel_window_pid = stWindowSet_.vec_hwnd[0].pid;
	}
	return hWnd;
}

int CaptureTool::GetSelWindowPid()
{
	return sel_window_pid;
}

bool CaptureTool::CaptureScrenn()
{
	CaptureTool::inst().GetScreenPixel(wow_hwnd_);
	if (is_real_white_)
	{
		return true;
	}
	else
	{
		ResetData();
	}
	return false;
}

bool CaptureTool::IsWindowEnable()
{
	return IsWindow(wow_hwnd_);
}

HWND CaptureTool::GetWowHWnd()
{
	return wow_hwnd_;
}

void CaptureTool::EnumDisplayDevices()
{
	DISPLAY_DEVICEA lpDisplayDevice;
	ZeroMemory(&lpDisplayDevice, sizeof(DISPLAY_DEVICEA));
	lpDisplayDevice.cb = sizeof(DISPLAY_DEVICEA);
	DWORD iDevNum = 0;
	for (iDevNum = 0; ::EnumDisplayDevicesA(NULL, iDevNum, &lpDisplayDevice, 0); iDevNum++)
	{
		std::string DeviceName = lpDisplayDevice.DeviceName;
		std::string DeviceString = lpDisplayDevice.DeviceString;
		std::string buff;
		buff.resize(1024);
		sprintf_s(&buff[0], 1024, "device: [%s] string: [%s]\n", DeviceName.c_str(), DeviceString.c_str());
		OutputDebugStringA(buff.c_str());
	}
}


bool CaptureTool::SetSelWindowIndex(int val)
{
	if (val >= 0 && val < stWindowSet_.vec_hwnd.size())
	{
		sel_window_pid = stWindowSet_.vec_hwnd[val].pid;
		return true;
	}
	return false;
}

CaptureTool::StWindowSet& CaptureTool::GetWindowSet()
{
	return stWindowSet_;
}

bool CaptureTool::IsPosWhiteImg(int y, BITMAP &bitmap, int x)
{
	bool is_white = true;
	for (int ic = 0; ic < 90; ic++)
	{
		DWORD& dwColor = *(DWORD*)&buff_cap_[y*bitmap.bmWidthBytes + (x + ic) * 4];
		unsigned char v = 256 - ic * 2 - 1;
		DWORD dwCompareColor = v + (v << 8) + (v << 16) + (0xFF << 24);
		if (dwColor != dwCompareColor)
		{
			is_white = false;
			break;
		}
	}
	if (is_white)
	{
		int y_new = y + 63;
		for (int ic = 0; ic < 90; ic++)
		{
			DWORD& dwColor = *(DWORD*)&buff_cap_[y_new*bitmap.bmWidthBytes + (x + ic) * 4];
			unsigned char v = 256 - ic * 2 - 1;
			DWORD dwCompareColor = v + (v << 8) + (v << 16) + (0xFF << 24);
			if (dwColor != dwCompareColor)
			{
				is_white = false;
				break;
			}
		}
	}
	return is_white;
}

void CaptureTool::GetScreenPixel(HWND hWnd)
{
	HWND hDesktop = ::GetDesktopWindow();
	ASSERT(hDesktop);
	if (NULL == hWnd)
	{
		hWnd = hDesktop;
	}
	int nWidht = -1;
	int nHeight = -1;
	// 这里可以优化, 只截取 有 信息的那一部分
	int cap_begin_x = 0;
	int cap_begin_y = 0;
	if (avail_scr_x != 0 && avail_scr_y != 0)
	{
		cap_begin_x = avail_scr_x;
		cap_begin_y = avail_scr_y;
		nWidht = 90;
		nHeight = 64;
	}
	else
	{
		RECT rect;
		::GetWindowRect(hWnd, &rect);
		nWidht = rect.right - rect.left;
		nHeight = rect.bottom - rect.top;
	}

	HDC hSrcDC = CreateDCA("\\\\.\\DISPLAY1", "NVIDIA GeForce GTX 1080", NULL, NULL);
	ASSERT(hSrcDC);
	HDC hMemDC = ::CreateCompatibleDC(hSrcDC);
	ASSERT(hMemDC);
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hSrcDC, nWidht, nHeight);
	ASSERT(hBitmap);
	HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hBitmap);
	::BitBlt(hMemDC, 0, 0, nWidht, nHeight, hSrcDC, cap_begin_x, cap_begin_y, SRCCOPY);
	BITMAP bitmap = { 0 };
	::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
	CopyValueFromSource(nWidht, nHeight, cap_begin_x, cap_begin_y, hBitmap, bitmap);
	//////////////////////////////////////////////////////////////////////////
	IsRealWhite(nWidht, nHeight, bitmap, cap_begin_x, cap_begin_y, hBitmap);
	//////////////////////////////////////////////////////////////////////////
	::SelectObject(hMemDC, hOldBitmap);
	::DeleteObject(hBitmap);
	::DeleteDC(hMemDC);
	::ReleaseDC(hWnd, hSrcDC);
}

void CaptureTool::CopyValueFromSource(int nWidht, int nHeight, int cap_begin_x, int cap_begin_y, HBITMAP hBitmap, BITMAP& bitmap)
{
	CBitmap obj_bitmap;
	obj_bitmap.Attach(hBitmap);
	obj_bitmap.FromHandle(hBitmap);
	DWORD bit_size = obj_bitmap.GetBitmap(&bitmap);
	DWORD size = bitmap.bmWidthBytes * bitmap.bmHeight;
	if (buff_cap_.size() != size)
	{
		buff_cap_.resize(size);
	}
	unsigned char *buff = &buff_cap_[0];
	int copy_value = 0;
	if (size)
	{
		copy_value = obj_bitmap.GetBitmapBits(size, buff);
	}
}

void CaptureTool::CopyWhiteImg(int cap_begin_y, BITMAP &bitmap, int cap_begin_x)
{
	const int avail_cap_size = 64 * 90 * 4;
	if (avail_cap_.size() != avail_cap_size)
	{
		avail_cap_.resize(avail_cap_size);
	}
	const int widthBytes = 90 * 4;
	for (int ic = 0; ic < 64; ic++)
	{
		memcpy_s(&avail_cap_[ic * widthBytes]
			, widthBytes
			, &buff_cap_[(cap_begin_y + ic)*bitmap.bmWidthBytes + cap_begin_x * 4]
			, widthBytes);
	}
}

bool CaptureTool::IsCapWhiteImg()
{
	bool is_white = true;
	for (int ic = 0; ic < 90; ic++)
	{
		DWORD& dwColor = *(DWORD*)&buff_cap_[ic * 4];
		unsigned char v = 256 - ic * 2 - 1;
		DWORD dwCompareColor = v + (v << 8) + (v << 16) + (0xFF << 24);
		if (dwColor != dwCompareColor)
		{
			is_white = false;
			break;
		}
	}
	if (is_white)
	{
		int y_new = 63;
		for (int ic = 0; ic < 90; ic++)
		{
			int y_begin = y_new * 90 * 4;
			DWORD& dwColor = *(DWORD*)&buff_cap_[y_begin + ic * 4];
			unsigned char v = 256 - ic * 2 - 1;
			DWORD dwCompareColor = v + (v << 8) + (v << 16) + (0xFF << 24);
			if (dwColor != dwCompareColor)
			{
				is_white = false;
				break;
			}
		}
	}
	return is_white;
}


void CaptureTool::IsRealWhite(int nWidht, int nHeight, BITMAP& bitmap, int cap_begin_x, int cap_begin_y, HBITMAP hBitmap)
{
	is_real_white_ = false;
	if (nWidht != 90 && nHeight != 6)
	{
		for (int y = 0; y < nHeight - 64; y++)
		{
			for (int x = 0; x < nWidht - 90; x++)
			{
				if (IsPosWhiteImg(y, bitmap, x))
				{
					avail_scr_x = x;
					avail_scr_y = y;
					is_real_white_ = true;
					CopyWhiteImg(avail_scr_y, bitmap, avail_scr_x);
					break;
				}
			}
			if (is_real_white_)
			{
				break;;
			}
		}
	}
	else
	{
		if (IsCapWhiteImg())
		{
			is_real_white_ = true;
			CopyWhiteImg(0, bitmap, 0);
		}
	}
}

BOOL SaveHwndToBmpFile(HWND hWnd, LPCTSTR lpszPath)
{
	HWND hDesktop = ::GetDesktopWindow();
	ASSERT(hDesktop);
	if (NULL == hWnd)
	{
		hWnd = hDesktop;
	}
	RECT rect;
	::GetWindowRect(hWnd, &rect);
	int nWidht = rect.right - rect.left;
	int nHeight = rect.bottom - rect.top;
	//HDC hSrcDC = ::GetWindowDC(hWnd);
	//HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
	HDC hSrcDC = CreateDCA("DISPLAY", NULL, NULL, NULL);
	ASSERT(hSrcDC);
	HDC hMemDC = ::CreateCompatibleDC(hSrcDC);
	ASSERT(hMemDC);
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hSrcDC, nWidht, nHeight);
	ASSERT(hBitmap);
	HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hBitmap);
	::BitBlt(hMemDC, 0, 0, nWidht, nHeight, hSrcDC, 0, 0, SRCCOPY);
	BITMAP bitmap = { 0 };
	::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
	BITMAPINFOHEADER bi = { 0 };
	BITMAPFILEHEADER bf = { 0 };
	CONST int nBitCount = 24;
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = bitmap.bmWidth;
	bi.biHeight = bitmap.bmHeight;
	bi.biPlanes = 1;
	bi.biBitCount = nBitCount;
	bi.biCompression = BI_RGB;
	DWORD dwSize = ((bitmap.bmWidth * nBitCount + 31) / 32) * 4 * bitmap.bmHeight;
	HANDLE hDib = GlobalAlloc(GHND, dwSize + sizeof(BITMAPINFOHEADER));
	LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
	*lpbi = bi;
	CBitmap bitmap_o;
	bitmap_o.Attach(hBitmap);
	bitmap_o.FromHandle(hBitmap);
	::GetDIBits(hMemDC, hBitmap, 0, bitmap.bmHeight, (BYTE*)lpbi + sizeof(BITMAPINFOHEADER), (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
	COLORREF cf = GetPixel(hMemDC, 0, 0);
	try
	{
		CFile file;
		file.Open(lpszPath, CFile::modeCreate | CFile::modeWrite);
		bf.bfType = 0x4d42;
		dwSize += sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
		bf.bfSize = dwSize;
		bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
		file.Write((BYTE*)&bf, sizeof(BITMAPFILEHEADER));
		file.Write((BYTE*)lpbi, dwSize);
		file.Close();
	}
	catch (CFileException* e)
	{
		e->ReportError();
		e->Delete();
	}
	GlobalUnlock(hDib);
	GlobalFree(hDib);
	::SelectObject(hMemDC, hOldBitmap);
	::DeleteObject(hBitmap);
	::DeleteDC(hMemDC);
	::ReleaseDC(hWnd, hSrcDC);
	return TRUE;
}
